package com.koron.util;

import com.koron.common.bean.EnumDBBean;
import com.koron.common.bean.EnumDetailDBBean;
import com.koron.common.mapper.UserDefineMapper;
import com.koron.common.web.bean.StaffBean;
import com.koron.common.web.mapper.StaffMapper;
import com.koron.css2.ApplicationConfig;
import com.koron.css2.baseConfig.impl.GenerateNumber;
import com.koron.css2.baseConfig.impl.GenerateNumberImpl;
import com.koron.css2.baseConfig.mapper.BusinessAreaMapper;
import com.koron.css2.baseConfig.mapper.ConfigMapper;
import com.koron.css2.baseConfig.mapper.WaterPriceMapper;
import com.koron.css2.baseConfig.mapper.WaterTypeMapper;
import com.koron.css2.clientCenter.bean.UserChangeItemBean;
import com.koron.css2.clientCenter.mapper.AccountInfoMapper;
import com.koron.css2.clientCenter.mapper.CtmInfoMapper;
import com.koron.css2.clientCenter.mapper.UserInfoMapper;
import com.koron.css2.meterReading.queryBean.BaseReadCycleQueryBean;
import com.koron.css2.meterReading.vo.BaseReadCycleVo;
import com.koron.css2.serviceManage.bean.FieldComparisonBean;
import com.koron.css2.serviceManage.bean.SelectBean;
import com.koron.css2.serviceManage.bean.UserFieldBean;
import com.koron.css2.serviceManage.bean.UserInfoBean;
import com.koron.css2.serviceManage.mapper.BaseReadCycleMapper;
import com.koron.css2.serviceManage.mapper.DictionaryMapper;
import com.koron.css2.serviceManage.mapper.FieldComparisonMapper;
import com.koron.css2.serviceManage.mapper.UserFieldConfigMapper;
import com.koron.css2.serviceManage.utils.JsonUtils;
import com.koron.css2.serviceManage.utils.RedisUtils;
import com.koron.css2.serviceManage.vo.DictionaryVO;
import com.koron.css2.systemManage.bean.OrgUserBean;
import com.koron.css2.systemManage.dto.UserAuthorityDto;
import com.koron.css2.systemManage.mapper.OrgUserMapper;
import com.koron.css2.workflow.ProcessStatusEnum;
import com.koron.ebs.permission.StaffAccount;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bson.types.ObjectId;
import org.koron.ebs.businessLog.LogBean;
import org.koron.ebs.common.KVBean;
import org.koron.ebs.module.ModuleService;
import org.koron.ebs.mybatis.ADOConnection;
import org.koron.ebs.mybatis.MybatisInfo;
import org.koron.ebs.mybatis.SessionFactory;
import org.koron.ebs.mybatis.TaskAnnotation;
import org.koron.ebs.util.field.EnumElement;
import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.servlet.ModelAndView;
import org.swan.bean.MessageBean;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.sql.Timestamp;
import java.text.NumberFormat;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

public class Tools {
    public static final ServletContext context = ContextLoader.getCurrentWebApplicationContext().getServletContext();

    private static Logger logger = LogManager.getLogger(Tools.class);

    private static ModuleService service = null;
    private static final long DAY_MINUTES = 24 * 60 * 60 * 1000;

    /**
     * 用户编号生成规则的编号，唯一
     */
    public static final String USER_NO_RULE_CODE = "USER_INFO";

    /**
     * 客户编号生成规则的编号，唯一
     */
    public static final String CTM_NO_RULE_CODE = "USER_CTM";

    /**
     * 账户编号生成规则的编号，唯一
     */
    public static final String ACCOUNT_NO_RULE_CODE = "USER_ACCOUNT";

    /**
     * 用于设置用户编号重复，重新生成的次数，默认10次
     */
    public static final int GEN_COUNT = 10;

    /**
     * 获取用户登录信息(包括用户信息，用户可选水司信息，用户当前水司信息) redis可用从redis中获取，不可用或者redis中没数据，从数据库获取
     *
     * @param token
     * @return
     */
    public static UserInfoBean getLoginBean(String token) {
        if (StringUtils.isEmpty(token)) {
            return null;
        }
        try {
            String value = RedisUtils.get(token);
            RedisUtils.expire(token, ApplicationConfig.getExpireTime());
            if (StringUtils.isNotBlank(value)) {
                return JsonUtils.jsonToPojo(value, UserInfoBean.class);
            }
        } catch (Exception ex) {
            logger.error("通过redis 缓存获取用户信息失败:" + ex.getMessage(), ex);
        }
        return null;
    }

    /**
	 * 获取用户登录信息(包括用户信息，用户可选水司信息，用户当前水司信息) redis可用从redis中获取，不可用或者redis中没数据，从数据库获取
	 * 
	 * @param token
	 * @param account
	 * @return
	 */
	public static UserAuthorityDto getUserAuthority(String account, String token) {
		String value = null;
		try {
			value = RedisUtils.get("user_auth_" + token + "_" + account);
			RedisUtils.expire("user_auth_" + token + "_" + account, ApplicationConfig.getExpireTime());
			if(StringUtils.isNotBlank(value)) {
				if(value.contains("\\")) {  // 假如取出的对象中包含反斜杠
					value = value.substring(1, value.length() - 1);
					value = value.replaceAll("\\\\", "");
				}
				return JsonUtils.jsonToPojo(value, UserAuthorityDto.class);
			}
		}catch(Exception ex) {
			ex.printStackTrace();
			logger.error("通过redis 缓存获取用户权限信息失败:" + ex.getMessage() + " 用户信息：" + value);
		}
		return null;
	}

    /**
     * 获取模块服务组件
     *
     * @return
     */
    public static final ModuleService getModuleService() {
        if (service == null) {
            WebApplicationContext context2 = WebApplicationContextUtils.getWebApplicationContext(context);
            return (ModuleService) context2.getBean("moduleService");
        }
        return service;
    }

    public static OrgUserBean getOrgUserByAccount(String env, String account) {
        return ADOConnection.runTask(factory -> {
            OrgUserMapper mapper = factory.getMapper(OrgUserMapper.class, env);
            return mapper.selectByAccount(account);
        }, OrgUserBean.class);
    }

    public static StaffBean getStaffBean(String account) {
        return ADOConnection.runTask(factory -> {
            StaffMapper staffMapper = factory.getMapper(StaffMapper.class, "_default");
            return staffMapper.getStaff(account);
        }, StaffBean.class);
    }

    public static StaffBean getStaffByCode(String code) {
        return ADOConnection.runTask(factory -> {
            StaffMapper staffMapper = factory.getMapper(StaffMapper.class, "_default");
            return staffMapper.getStaffByCode(code);
        }, StaffBean.class);
    }
    
    /**
     * 获取表账号登录 用于流程驳回
     */
    public static OrgUserBean getOrgUserTempByAccount(String env, String account) {
        return ADOConnection.runTask(factory -> {
            OrgUserMapper mapper = factory.getMapper(OrgUserMapper.class, env);
            return mapper.selectTempByAccount(account);
        }, OrgUserBean.class);
    }

    /**
     * 获取参数
     *
     * @param dbEnv      数据源
     * @param configName 参数名称
     * @return
     */
    public static String getConfigValue(String dbEnv, String configName) {
        return ADOConnection.runTask(dbEnv, factory -> {
            ConfigMapper configMapper = factory.getMapper(ConfigMapper.class);
            return StringUtils.defaultString(configMapper.getConfigValueByName(configName));
        }, String.class);
    }

    /**
     * 获取数据库连接
     *
     * @return
     */
    public static final SessionFactory getSessionFactory() {
        MessageBean<SessionFactory> msgBean = getModuleService().invoke(MybatisInfo.MODULENAME, "getSessionFactory",
                SessionFactory.class);
        return msgBean.getCode() == 0 ? msgBean.getData() : null;
    }

    /**
     * 取得调用该函数的类的包下name对应的文件. 使用 {@link #getView(String, Class)}代替
     *
     * @param name  名称
     * @param layer 线程中的层数，默认为3
     * @return
     */
    @Deprecated
    public static final ModelAndView getView(String name, int layer) {
        StackTraceElement[] els = Thread.currentThread().getStackTrace();
        if (els == null || els.length < layer + 1)
            return new ModelAndView("/" + name);
        StackTraceElement el = els[layer];
        String pkgStr = el.getClassName();
        if (pkgStr.indexOf('.') != -1) {
            pkgStr = pkgStr.substring(0, pkgStr.lastIndexOf('.'));
            return new ModelAndView("/" + pkgStr.replace('.', '/') + '/' + name);
        } else {
            return new ModelAndView("/" + name);
        }
    }

    /**
     * 取得调用该函数的类的包下name对应的文件. 使用 {@link #getView(String, Class)}代替
     *
     * @param name  名称
     * @param clazz 对应类
     * @return 类对应目录下的name文件作为视图
     */
    public static final ModelAndView getView(String name, Class<?> clazz) {
        String pkgStr = clazz.getPackage().getName();
        return new ModelAndView("/" + pkgStr.replace('.', '/') + '/' + name);
    }

    /**
     * 取得调用该函数的类的包下name对应的文件
     *
     * @param name 名称
     * @return
     */
    public static final ModelAndView getView(String name) {
        return getView(name, 3);
    }

    /**
     * 获取MD5后的字符串
     *
     * @param source 进行加密的字符串
     * @return
     */
    public static String MD5(String source) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(source.getBytes());
            byte[] b = md.digest();
            StringBuffer sb = new StringBuffer();
            for (byte c : b) {
                int val = ((int) c) & 0xff;
                if (val < 16)
                    sb.append("0");
                sb.append(Integer.toHexString(val));
            }
            return sb.toString().toUpperCase();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 生成票据编号 不需要水司编号
     *
     * @param factory
     * @param ruleCode
     * @return
     */
    public static String getBillNo(SessionFactory factory, String ruleCode) {
        GenerateNumber generateNumber = new GenerateNumberImpl();
        return generateNumber.generate(factory, ruleCode);
    }

    /**
     * 生成票据编号 需要水司编号
     *
     * @param factory
     * @param ruleCode
     * @param waterCode
     * @return
     */
    public static String getBillNo(SessionFactory factory, String ruleCode, String waterCode) {
        GenerateNumber generateNumber = new GenerateNumberImpl();
        return generateNumber.generate(factory, ruleCode, waterCode);
    }

    /**
     * 生成票据编号，如果是生成用户编号，则会自动判断生成的用户编号是否已存在
     * USER_INFO 表以及 USER_INFO_TEMP 表，如果存在则递归继续生成下一个
     * 用户编号，但是生成的次数不能超过10次，否则抛异常生成用户编号失败
     *
     * @param factory 数据源
     * @param waterCode 水司CODE 可以为空
     * @return
     */
    public static String getUserNo(SessionFactory factory, String waterCode){
        return getUserNo(factory, waterCode, 0);
    }

    /**
     * 生成票据编号，如果是生成用户编号，则会自动判断生成的用户编号是否已存在
     * CTM_INFO 表以及 CTM_INFO_TEMP 表，如果存在则递归继续生成下一个
     * 用户编号，但是生成的次数不能超过10次，否则抛异常生成用户编号失败
     *
     * @param factory 数据源
     * @param waterCode 水司CODE 可以为空
     * @return
     */
    public static String getCtmNo(SessionFactory factory, String waterCode){
        return getCtmNo(factory, waterCode, 0);
    }

    /**
     * 生成票据编号，如果是生成用户编号，则会自动判断生成的用户编号是否已存在
     * ACCOUNT_INFO 表以及 ACCOUNT_INFO_TEMP 表，如果存在则递归继续生成下一个
     * 用户编号，但是生成的次数不能超过10次，否则抛异常生成用户编号失败
     *
     * @param factory 数据源
     * @param waterCode 水司CODE 可以为空
     * @return
     */
    public static String getAccountNo(SessionFactory factory, String waterCode){
        return getAccountNo(factory, waterCode, 0);
    }

    /**
     * 生成票据编号，如果是生成用户编号，则会自动判断生成的用户编号是否已存在
     * USER_INFO 表以及 USER_INFO_TEMP 表，如果存在则递归继续生成下一个
     * 用户编号，但是生成的次数不能超过10次，否则抛异常生成用户编号失败
     *
     * @param factory 数据源
     * @param waterCode 水司CODE 可以为空
     * @param count 用于计数，生成的次数
     * @return
     */
    private static String getUserNo(SessionFactory factory, String waterCode, int count) {
        String userNo = getBillNo(factory, USER_NO_RULE_CODE, waterCode);
        UserInfoMapper userInfoMapper = factory.getMapper(UserInfoMapper.class);
        // 判断已生成的用户编号是否已存在 USER_INFO 表以及 USER_INFO_TEMP 表
        int iCount = userInfoMapper.countByUserNo(userNo);
        if (iCount > 0){
            // 递归的次数超过设置的次数，则抛异常退出循环
            if (count > GEN_COUNT){
                throw new RuntimeException("生成的用户编号【" + userNo + "】重复");
            }
            count = count + 1;
            // 递归
            return getUserNo(factory, waterCode, count);
        }
        return userNo;
    }

    /**
     * 生成票据编号，如果是生成用户编号，则会自动判断生成的用户编号是否已存在
     * CTM_INFO 表以及 CTM_INFO_TEMP 表，如果存在则递归继续生成下一个
     * 用户编号，但是生成的次数不能超过10次，否则抛异常生成用户编号失败
     *
     * @param factory 数据源
     * @param waterCode 水司CODE 可以为空
     * @param count 用于计数，生成的次数
     * @return
     */
    private static String getCtmNo(SessionFactory factory, String waterCode, int count) {
        String ctmNo = getBillNo(factory, CTM_NO_RULE_CODE, waterCode);
        CtmInfoMapper ctmInfoMapper = factory.getMapper(CtmInfoMapper.class);
        // 判断已生成的客户编号是否已存在 CTM_INFO 表以及 CTM_INFO_TEMP 表
        int iCount = ctmInfoMapper.countByCtmNo(ctmNo);
        if (iCount > 0){
            // 递归的次数超过设置的次数，则抛异常退出循环
            if (count > GEN_COUNT){
                throw new RuntimeException("生成的客户编号【" + ctmNo + "】重复");
            }
            count = count + 1;
            // 递归
            return getCtmNo(factory, waterCode, count);
        }
        return ctmNo;
    }

    /**
     * 生成票据编号，如果是生成用户编号，则会自动判断生成的用户编号是否已存在
     * ACCOUNT_INFO 表以及 ACCOUNT_INFO_TEMP 表，如果存在则递归继续生成下一个
     * 用户编号，但是生成的次数不能超过10次，否则抛异常生成用户编号失败
     *
     * @param factory 数据源
     * @param waterCode 水司CODE 可以为空
     * @param count 用于计数，生成的次数
     * @return
     */
    private static String getAccountNo(SessionFactory factory, String waterCode, int count) {
        String accountNo = getBillNo(factory, ACCOUNT_NO_RULE_CODE, waterCode);
        AccountInfoMapper accountInfoMapper = factory.getMapper(AccountInfoMapper.class);
        // 判断已生成的客户编号是否已存在 ACCOUNT_INFO 表以及 ACCOUNT_INFO_TEMP 表
        int iCount = accountInfoMapper.countByAccountNo(accountNo);
        if (iCount > 0){
            // 递归的次数超过设置的次数，则抛异常退出循环
            if (count > GEN_COUNT){
                throw new RuntimeException("生成的账户户编号【" + accountNo + "】重复");
            }
            count = count + 1;
            // 递归
            return getAccountNo(factory, waterCode, count);
        }
        return accountNo;
    }

    public static String getSameUserNo(SessionFactory factory, String ruleCode, String waterCode) {
        GenerateNumber generateNumber = new GenerateNumberImpl();
        String userNo= generateNumber.generate(factory, ruleCode, waterCode);
        UserInfoMapper mapper = factory.getMapper(UserInfoMapper.class,DBSourceUtils.getDbEnv(waterCode));
        int count = mapper.checkNoFilter(userNo,userNo.replaceFirst("1","2"),userNo.replaceFirst("1","3"));
        if(count>0){
            userNo = getSameUserNo(factory, ruleCode, waterCode);
        }
        return userNo;
    }
    public static String getSameCtmNo(String userNo) {
        return userNo.replaceFirst("1","2");
    }
    public static String getSameAccountNo(String userNo) {
        return userNo.replaceFirst("1","3");
    }

    public static LogBean getLogBean(HttpServletRequest request) {
        LogBean bean = new LogBean();
        bean.setIp(request.getHeader("X-Real-IP"));
        bean.setMacaddress(request.getRemoteHost());
        if (request.getSession().getAttribute(Constant.USER) != null)
            bean.setUsername(((StaffAccount) request.getSession().getAttribute(Constant.USER)).getStaff().getName());
        return bean;
    }

    /**
     * 通过code 获取数据字典Map集合
     *
     * @param factory
     * @param code
     * @return 数据字典集合
     */
    public static Map<String, String> mapDicByCode(SessionFactory factory, String code) {
        List<DictionaryVO> dictionarys = listDicByCode(factory, code);
        return dictionarys.stream().collect(Collectors.toMap(DictionaryVO::getValue, DictionaryVO::getName));
    }

    /**
     *  获取抄表周期相关信息
     *
     * @param factory
     * @return 抄表周期相关信息
     */

    public static Map<String, String> readCycleByCode(SessionFactory factory) {
        BaseReadCycleMapper readCycleMapper = factory.getMapper(BaseReadCycleMapper.class);
        List<BaseReadCycleVo> baseReadCycleVos = readCycleMapper.selectReadCycleList(new BaseReadCycleQueryBean());
        return baseReadCycleVos.stream().collect(Collectors.toMap(BaseReadCycleVo::getId, BaseReadCycleVo::getName));
    }

    /**
     * 通过code 获取数据字典集合
     *
     * @param factory
     * @param code
     * @return 数据字典集合
     */
    public static List<DictionaryVO> listDicByCode(SessionFactory factory, String code) {
        Objects.requireNonNull(code, "数据字典code参数不能为空");
        DictionaryMapper mapper = factory.getMapper(DictionaryMapper.class);
        return mapper.findDictionaryParamName(code);
    }

    /**
     * 通过数据字典code以及字典value获取数据字典的name
     *
     * @param factory
     * @param code
     * @param value
     * @return 数据字典的value
     */
    public static String getDicNameByCodeAndValue(SessionFactory factory, String code, String value) {
        List<DictionaryVO> dics = listDicByCode(factory, code);
        DictionaryVO dic = dics.stream().filter(t -> t.getValue().equals(value)).findFirst().orElse(new DictionaryVO());
        return dic.getName();
    }

    /**
     * 通过数据字典code以及字典name获取数据字典的value
     *
     * @param factory
     * @param code
     * @param name
     * @return 数据字典的value
     */
    public static String getDicValueByCodeAndName(SessionFactory factory, String code, String name) {
        List<DictionaryVO> dics = listDicByCode(factory, code);
        DictionaryVO dic = dics.stream().filter(t -> t.getName().equals(name)).findFirst().orElse(new DictionaryVO());
        return dic.getValue();
    }

    /**
     * 获取表字段中英文键值对
     *
     * @param factory
     * @param table
     * @return 数据字典集合
     */
    public static Map<String, String> getFiledName(SessionFactory factory, String table) {
        FieldComparisonMapper fieldMapper = factory.getMapper(FieldComparisonMapper.class);
        // 整合键值对
        List<FieldComparisonBean> list = fieldMapper.selectFieldComparison(table);
        if (list == null) {
            return null;
        }
        Map<String, String> map = new HashMap<String, String>();
        for (FieldComparisonBean field : list) {
            //转为小写
            field.setColumnEn(field.getColumnEn().toLowerCase());
            //EN转驼峰
            map.put(Tools.camel(field.getColumnEn()), field.getColumnZh());
        }
        return map;
    }


    /**
     * 用户信息变更配置获取
     *
     * @param factory
     * @return 数据字典集合
     */
    public static Map<String, String> getChangeFiledName(SessionFactory factory, String pageName,String groupCode) {
        UserFieldConfigMapper userFieldConfigMapper = factory.getMapper(UserFieldConfigMapper.class,"_default");
        // 整合键值对
        List<UserFieldBean> list = userFieldConfigMapper.findFieldNameByPageNameAndGroupCode(pageName,groupCode);
        if (list == null) {
            return null;
        }
        Map<String, String> map = new HashMap<String, String>();
        for (UserFieldBean userFieldBean : list) {
            //生成字典
            map.put(userFieldBean.getFieldCode(),userFieldBean.getFieldName());
        }
        return map;
    }


    /**
     * 下划线转驼峰
     *
     * @param str
     * @return
     */
    public static String camel(String str) {
        // 利用正则删除下划线，把下划线后一位改成大写
        Pattern pattern = Pattern.compile("_(\\w)");
        Matcher matcher = pattern.matcher(str);
        StringBuffer sb = new StringBuffer(str);
        if (matcher.find()) {
            sb = new StringBuffer();
            // 将当前匹配子串替换为指定字符串，并且将替换后的子串以及其之前到上次匹配子串之后的字符串段添加到一个StringBuffer对象里。
            // 正则之前的字符和被替换的字符
            matcher.appendReplacement(sb, matcher.group(1).toUpperCase());
            // 把之后的也添加到StringBuffer对象里
            matcher.appendTail(sb);
        } else {
            return sb.toString();
        }
        return camel(sb.toString());
    }

    /**
     * 驼峰转下划线
     *
     * @param str
     * @return
     */
    public static String underline(String str) {
        Pattern pattern = Pattern.compile("[A-Z]");
        Matcher matcher = pattern.matcher(str);
        StringBuffer sb = new StringBuffer(str);
        if (matcher.find()) {
            sb = new StringBuffer();
            // 将当前匹配子串替换为指定字符串，并且将替换后的子串以及其之前到上次匹配子串之后的字符串段添加到一个StringBuffer对象里。
            // 正则之前的字符和被替换的字符
            matcher.appendReplacement(sb, "_" + matcher.group(0).toLowerCase());
            // 把之后的也添加到StringBuffer对象里
            matcher.appendTail(sb);
        } else {
            return sb.toString();
        }
        return underline(sb.toString());
    }

    /**
     * 获取枚举
     *
     * @param key 枚举的key
     * @return
     */
    @SuppressWarnings("unchecked")
    public static final EnumElement<Object> getEnumByKey(String key, Object... parameter) {
        try (SessionFactory factory = new SessionFactory()) {
            return factory.runTask(new Tools(), "getEnum", EnumElement.class, key, parameter);
        }
    }

    /**
     * @param factory
     * @param key
     * @return
     */
    @TaskAnnotation("getEnum")
    private EnumElement<Object> getEnumByKey(SessionFactory factory, String key, Object... parameter) {
        EnumElement<Object> ret = Constant.enumCache.get(key);
        if (parameter == null || parameter.length == 0) {
            if (ret != null)
                return ret;
        }
        logger.debug("获取指定的枚举值：" + key);
        UserDefineMapper mapper = factory.getMapper(UserDefineMapper.class);
        EnumDBBean bean = mapper.getEnumByKey(key);
        ret = new EnumElement<>();
        ret.setBit(bean.getIsbit());
        ret.setType(bean.getType());
        if (bean.getParam() != null && !bean.getParam().isEmpty()) {
            Map<String, String> map = getDyn(bean.getParam(), parameter);
            for (Entry<String, String> item : map.entrySet()) {
                switch (bean.getType()) {
                    case 0:
                        ret.put(item.getKey(), item.getValue());
                        break;
                    case 1:
                        ret.put(Integer.parseInt(item.getKey()), item.getValue());
                        break;
                    case 2:
                        ret.put(Long.parseLong(item.getKey()), item.getValue());
                }
            }
        } else {
            List<EnumDetailDBBean> list = mapper.getEnumDetailByEnumId(bean.getId());
            for (EnumDetailDBBean enumDetailDBBean : list) {
                switch (bean.getType()) {
                    case 0:
                        ret.put(enumDetailDBBean.getKey(), enumDetailDBBean.getValue());
                        break;
                    case 1:
                        ret.put(Integer.parseInt(enumDetailDBBean.getKey()), enumDetailDBBean.getValue());
                        break;
                    case 2:
                        ret.put(Long.parseLong(enumDetailDBBean.getKey()), enumDetailDBBean.getValue());
                }
            }
        }
        if (parameter == null || parameter.length == 0)
            Constant.enumCache.put(key, ret);
        return ret;
    }

    /**
     * 根据类名#方法的方式获取得KVBean数组，然后转换成map
     *
     * @param url       mybatis的接口名加方法名,方法不带参数，返回必须为List<KVBean>
     * @param parameter 参数，调用接口用的参数
     * @return
     */
    public static final LinkedHashMap<String, String> getDyn(String url, Object... parameter) {
        if (url == null || url.indexOf('#') == -1)
            return null;
        String className = url.substring(0, url.indexOf('#'));
        String methodName = url.substring(url.indexOf('#') + 1);
        try {
            SessionFactory factory = new SessionFactory();
            Class<?> clazz = Class.forName(className);
            Class<?>[] param = null;
            if (parameter != null) {
                param = new Class[parameter.length];
                for (int i = 0; i < parameter.length; i++) {
                    param[i] = parameter[i].getClass();
                }
            }
            Method m = clazz.getMethod(methodName, param);
            @SuppressWarnings("unchecked")
            List<KVBean> list = (List<KVBean>) m.invoke(factory.getMapper(clazz), parameter);
            LinkedHashMap<String, String> map = new LinkedHashMap<String, String>();
            for (KVBean kvBean : list) {
                map.put(kvBean.getKey(), kvBean.getValue());
            }
            factory.close();
            return map;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 生成16位key
     *
     * @return
     */
    public static String get16Key() {
        String uuid = UUID.randomUUID().toString().replaceAll("-", "");
        char s;
        String key = "";
        Random ran = new Random();
        for (int i = 0; i < 16; i++) {
            int index = ran.nextInt(32);
            char[] chars = uuid.toCharArray();
            s = chars[index];
            key += s;
        }
        return key.toUpperCase();
    }

    /**
     * 获取web目录的文件
     *
     * @param path 文件路径
     * @return
     */
    public static final File getWebFile(String path) {
        String url = context.getRealPath(path);
        File f = new File(url);
        if (f.exists())
            return f;
        return null;
    }

    /**
     * 转换单据状态字段
     *
     * @param status
     * @return
     */
    public static String transProcessStatus(String status) {
        String result = "进行中";
        if (StringUtils.isEmpty(status)) {
            result = "草稿";
        }
        if (Objects.equals(status, "END")) {
            result =  "已办结";
        }
        //if (Objects.equals(status, "CANCEL")) {
        //    result = "已废弃";
        //}
        if (Objects.equals(status, ProcessStatusEnum.TERMINATION.getStatus())) {
            result = "已终止";
        }
        if (Objects.equals(status, "suspend")) {
            result = "已挂起";
        }

        return result;
    }

    /**
     * 转换营收入帐类型字段
     *
     * @param type
     * @return
     */
    public static String transCssInType(Integer type) {
        if (Objects.equals(type, 1)) {
            return "银行转帐";
        } else if (Objects.equals(type, 2)) {
            return "现金缴存";
        } else if (Objects.equals(type, 3)) {
            return "第三方转帐";
        } else {
            return "";
        }
    }

    /**
     * 转换对账状态字段
     *
     * @param status
     * @return
     */
    public static String transBillStatus(Integer status) {
        if (Objects.equals(status, 0)) {
            return "未配对";
        } else if (Objects.equals(status, 1)) {
            return "自动配对";
        } else if (Objects.equals(status, 2)) {
            return "手动配对";
        } else if (Objects.equals(status, 3)) {
            return "非水费";
        } else if (Objects.equals(status, 4)) {
            return "其他水费";
        } else if (Objects.equals(status, 9)){
            return "POS交易";
        }else if (Objects.equals(status, 11)){
            return "POS交易未达已销";
        }else {
            return "状态有误";
        }
    }

    /**
     * 将date转换成string
     * 日期格式yyyy-MM-dd HH:mm:ss
     *
     */
    public static String getStringDate(Date time) {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dateString = formatter.format(time);
        return dateString;
    }

    public static String formatDateToString(Date date) {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
        String dateString = formatter.format(date);
        return dateString;
    }

    public static Date getDate(String dateString) {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
        ParsePosition pos = new ParsePosition(0);
        logger.info(dateString);
        Date currentTime_2 = formatter.parse(dateString, pos);
        logger.info(currentTime_2.toString());
        return currentTime_2;
    }

    /**
     * 获取ObjectId
     *
     * @return
     */
    public static String getObjectId() {
        return ObjectId.get().toHexString();
    }

    /**
     * 补0
     *
     * @return
     */
    public static String lpad(int num, int digit) {
        return String.format("%0" + digit + "d", num);
    }

    /**
     * 提供精确的加法运算。
     *
     * @param value1 被加数
     * @param value2 加数
     * @return 两个参数的和
     */
    public static Double add(Double value1, Double value2) {
        BigDecimal b1 = new BigDecimal(Double.toString(value1));
        BigDecimal b2 = new BigDecimal(Double.toString(value2));
        return b1.add(b2).doubleValue();
    }

    /**
     * 获取当前日期（yyyy-MM-dd）
     *
     * @return
     */
    public static String getCurrentDate() {
        Calendar cal = Calendar.getInstance();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        return sdf.format(cal.getTime());
    }

    /**
     * 获取当前日期（yyyy年MM月dd日）
     *
     * @return
     */
    public static String getCurrentDateCN() {
        Calendar cal = Calendar.getInstance();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
        return sdf.format(cal.getTime());
    }

    /**
     * 获取当前时间（yyyy-MM-dd hh24:mi:ss）
     *
     * @return
     */
    public static String getCurrentTime() {
        Calendar cal = Calendar.getInstance();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return sdf.format(cal.getTime());
    }

    /**
     * 比较两个对象字段值
     *
     * @param obj1   对象1
     * @param obj2   对象2
     * @param fields 对比字段
     * @return
     * @throws IntrospectionException
     * @throws InvocationTargetException
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     */
    public static Map<String, List<Object>> compareFields(Object obj1, Object obj2, Map<String, String> fields) throws IntrospectionException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        Map<String, List<Object>> map = new HashMap<String, List<Object>>();
        // 只有两个对象都是同一类型的才有可比性
        if (obj1.getClass() == obj2.getClass()) {
            @SuppressWarnings("rawtypes")
            Class clazz = obj1.getClass();
            // 获取object的属性描述
            PropertyDescriptor[] pds = Introspector.getBeanInfo(clazz, Object.class).getPropertyDescriptors();
            // 这里就是所有的属性了
            for (PropertyDescriptor pd : pds) {
                // 属性名
                String name = pd.getName();
                // 如果当前属性选择进行比较，跳到下一次循环
                if (fields != null && fields.containsKey(name)) {
                    // get方法
                    Method readMethod = pd.getReadMethod();
                    // 在obj1上调用get方法等同于获得obj1的属性值
                    Object objBefore = readMethod.invoke(obj1);
                    // 在obj2上调用get方法等同于获得obj2的属性值
                    Object objAfter = readMethod.invoke(obj2);
                    if (objBefore instanceof Timestamp) {
                        objBefore = new Date(((Timestamp) objBefore).getTime());
                    }
                    if (objAfter instanceof Timestamp) {
                        objAfter = new Date(((Timestamp) objAfter).getTime());
                    }
                    if (StringUtils.isBlank(ObjectUtils.toString(objBefore)) && StringUtils.isBlank(ObjectUtils.toString(objAfter))) {
                        continue;
                    } else if (!StringUtils.isBlank(ObjectUtils.toString(objAfter)) && StringUtils.isBlank(ObjectUtils.toString(objBefore))) {
                        List<Object> list = new ArrayList<Object>();
                        list.add("空");
                        list.add(objAfter);
                        map.put(name, list);
                        continue;
                    } else if (StringUtils.isBlank(ObjectUtils.toString(objAfter)) && !StringUtils.isBlank(ObjectUtils.toString(objBefore))) {
                        List<Object> list = new ArrayList<Object>();
                        list.add(objBefore);
                        list.add("空");
                        map.put(name, list);
                        continue;
                    }
                    // 比较这两个值是否相等,不等则放入map
                    if (!objBefore.equals(objAfter)) {
                        List<Object> list = new ArrayList<Object>();
                        list.add(objBefore);
                        list.add(objAfter);
                        map.put(name, list);
                    }
                }
            }
        }
        return map;
    }

    /**
     * 比较两个对象变更字段并且拼接成字符串
     *
     * @param obj1   对象1
     * @param obj2   对象2
     * @param index  变更个数
     * @param fields 对比字段
     * @return
     * @throws IntrospectionException
     * @throws InvocationTargetException
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     */
    public static Map<String, String> getChangeItems(Object obj1, Object obj2, int index, Map<String, String> fields) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, IntrospectionException {
        String arrs = "";
        Map<String, String> map = new HashMap<String, String>();
        // 比较对象变更字段值
        Map<String, List<Object>> changes = compareFields(obj1, obj2, fields);
        if (changes != null && changes.size() != 0) {
            // 取出所有的变更项的主键
            Set<String> set = changes.keySet(); // 取出所有的key值
            for (String key : set) {
                if (!fields.containsKey(key)) {
                    continue;
                }
                index++;
                arrs += index + "." + fields.get(key) + "：" + changes.get(key).get(0) + "-->" + changes.get(key).get(1)
                        + "　";
            }
        }
        map.put("arrs", arrs);
        map.put("index", index + "");
        return map;
    }

    /**
     * 变更事项。
     *
     * @param userInfo
     * @param changeNos 传三户编号+单据id+单据类型
     * @param obj1
     * @param obj2
     * @param fields
     * @return
     * @throws IllegalAccessException
     * @throws IllegalArgumentException
     * @throws InvocationTargetException
     * @throws IntrospectionException
     */
    public static List<UserChangeItemBean> getChangeItems(UserInfoBean userInfo, Map<String, String> changeNos, Object obj1, Object obj2,
                                                          Map<String, String> fields) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, IntrospectionException {
        List<UserChangeItemBean> list = new ArrayList<UserChangeItemBean>();
        // 比较对象变更字段值
        Map<String, List<Object>> changes = Tools.compareFields(obj1, obj2, fields);
        if (changes != null && changes.size() != 0) {
            // 取出所有的变更项的主键
            Set<String> set = changes.keySet(); // 取出所有的key值
            for (String key : set) {
                if (!fields.containsKey(key)) {
                    continue;
                }
                UserChangeItemBean bean = new UserChangeItemBean();
                bean.setCreateInfo(userInfo);
                bean.setReceiptId(changeNos.get("receiptId"));
                bean.setReceiptType(changeNos.get("receiptType"));
                bean.setUserNo(changeNos.get("userNo"));
                bean.setCtmNo(changeNos.get("ctmNo"));
                bean.setAccountNo(changeNos.get("accountNo"));
                bean.setBillNo(changeNos.get("billNo"));
                bean.setChangeItem(fields.get(key));
                bean.setChangeCode(underline(key));
                bean.setOldItemValue(changes.get(key).get(0).toString());
                bean.setNewItemValue(changes.get(key).get(1).toString());
                list.add(bean);
            }
        }

        return list;
    }

    /**
     * 获取虚拟公式中的用户编号
     *
     * @param formulas
     * @return
     */
    public static List<String> getUserNoFromFormulas(String formulas) {
        List<String> codes = new ArrayList<String>();
        //String line = "[000002]+300+[000003]*50%";
        String regex = "\\[(.*?)]";
        // 创建 Pattern 对象
        Pattern r = Pattern.compile(regex);
        // 现在创建 matcher 对象
        Matcher m = r.matcher(formulas);
        while (m.find()) {
            codes.add(m.group(1));
        }
        return codes;

    }

    /**
     * 获取当前年份（yyyy）
     *
     * @return
     */
    public static String getYear() {
        Calendar cal = Calendar.getInstance();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy");
        return sdf.format(cal.getTime());
    }

    /**
     * 获取当前月份（yyyyMM）
     *
     * @return
     */
    public static String getYearMonth() {
        Calendar cal = Calendar.getInstance();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMM");
        return sdf.format(cal.getTime());
    }

    /**
     * 求最小值
     *
     * @param values 传入的参数，多个
     * @return
     */
    public static double getMin(double... values) {
        Double min = Double.MAX_VALUE;
        for (Double d : values) {
            if (d < min) {
                min = d;
            }
        }
        return min;
    }

    /**
     * 将一个List等分成n个list
     *
     * @param <T>
     * @param source
     * @param n
     * @return
     */
    public static <T> List<List<T>> averageAssign(List<T> source, int n) {
        List<List<T>> result = new ArrayList<>();
        //(先计算出余数)
        int remainder = source.size() % n;
        //然后是商
        int number = source.size() / n;
        //偏移量
        int offset = 0;
        for (int i = 0; i < n; i++) {
            List<T> value;
            if (remainder > 0) {
                value = source.subList(i * number + offset, (i + 1) * number + offset + 1);
                remainder--;
                offset++;
            } else {
                value = source.subList(i * number + offset, (i + 1) * number + offset);
            }
            result.add(value);
        }
        return result;
    }

    public static double getPercent(int num1, int num2) {
        if (num2 == 0) {
            return 0;
        }
        // 创建一个数值格式化对象
        NumberFormat numberFormat = NumberFormat.getInstance();
        // 设置精确到小数点后2位
        numberFormat.setMaximumFractionDigits(2);
        numberFormat.setGroupingUsed(false);
        String result = numberFormat.format((float) num1 / (float) num2 * 100);
        return Double.valueOf(result);
    }

    public static double getPercent(double double1, double double2) {
        if (double2 == 0) {
            return 0;
        }
        // 创建一个数值格式化对象
        NumberFormat numberFormat = NumberFormat.getInstance();
        // 设置精确到小数点后2位
        numberFormat.setMaximumFractionDigits(2);


        String result = numberFormat.format(double1 / double2 * 100);
        return Double.valueOf(result);
    }

    /**
     * 获取当前日期前n天的月份(yyyyMM)
     *
     * @return
     */
    public static String getBeforeDateMonth(long day) {
        // 将字符串转为日期
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMM");
        Calendar cal = Calendar.getInstance();
        long currentTimeMillis = cal.getTime().getTime();
        // 前day天
        currentTimeMillis = currentTimeMillis - day * DAY_MINUTES;
        cal.setTimeInMillis(currentTimeMillis);
        return sdf.format(cal.getTime());
    }

    /**
     * 获取当前日期前n天的年(yyyy)
     *
     * @return
     */
    public static String getBeforeDateYear(long day) {
        // 将字符串转为日期
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy");
        Calendar cal = Calendar.getInstance();
        long currentTimeMillis = cal.getTime().getTime();
        // 前day天
        currentTimeMillis = currentTimeMillis - day * DAY_MINUTES;
        cal.setTimeInMillis(currentTimeMillis);
        return sdf.format(cal.getTime());
    }

    /**
     * 获取当月第几天
     *
     * @return
     */
    public static int getCurrentMonthToDay() {
        Calendar cal = Calendar.getInstance();
        SimpleDateFormat sdf = new SimpleDateFormat("dd");
        return Integer.parseInt(sdf.format(cal.getTime()));
    }

    /**
     * 查询子营业区域
     * @param parentId
     * @param groupCode
     * @param mapper
     * @return
     */
    public static List<String> queryChildrenArea(String parentId, String groupCode, BusinessAreaMapper mapper){
        List<SelectBean> list = mapper.findBusinessAreaByParentId(parentId,groupCode);
        List<String> result = new ArrayList<>();
        result.add(parentId);
        for (SelectBean selectBean : list){
            List<String> sub = queryChildrenArea(selectBean.getId(),groupCode,mapper);
            result.addAll(sub);
        }
        return result;
    }

    /**
     * 查询子营业区域
     * @param parentId
     * @param groupCode
     * @param mapper
     * @return
     */
    public static List<String> queryChildrenAreaNo(String parentId, String groupCode,String areaNo, BusinessAreaMapper mapper){
        List<SelectBean> list = mapper.findBusinessAreaByParentId(parentId,groupCode);
        List<String> result = new ArrayList<>();
        result.add(areaNo);
        for (SelectBean selectBean : list){
            List<String> sub = queryChildrenAreaNo(selectBean.getId(),groupCode,selectBean.getCode(),mapper);
            result.addAll(sub);
        }
        return result;
    }

    /**
     * 查询子用水性质
     * @param parentId
     * @param waterTypeMapper
     * @return
     */
    public static List<String> queryChildrenWaterId(String parentId, WaterTypeMapper waterTypeMapper){
        List<SelectBean> list = waterTypeMapper.findNextIdByParentId(parentId);
        List<String> result = new ArrayList<>();
        result.add(parentId);
        for (SelectBean selectBean : list){
            List<String> sub = queryChildrenWaterId(selectBean.getId(),waterTypeMapper);
            result.addAll(sub);
        }
        return result;
    }

    /**
     * 查询子用水价格
     * @param parentId
     * @param waterPriceMapper
     * @return
     */
    public static List<String> queryChildrenWaterPriceId(String waterTypeId, String parentId, WaterPriceMapper waterPriceMapper){
        List<SelectBean> list = waterPriceMapper.findNextIdByParentId(waterTypeId, parentId);
        List<String> result = new ArrayList<>();
        result.add(parentId);
        for (SelectBean selectBean : list){
            List<String> sub = queryChildrenWaterPriceId(waterTypeId, selectBean.getId(),waterPriceMapper);
            result.addAll(sub);
        }
        return result;
    }

    public static String subZeroAndDot(String s) {
        if (s.indexOf(".") > 0) {
            s = s.replaceAll("0+?$", "");// 去掉多余的0
            s = s.replaceAll("[.]$", "");// 如最后一位是.则去掉
        }
        return s;
    }
    /**
     * 拼接2个字符串，为Null则用''代替
     */
    public static String StringValueOf(String str,String str1){
        return (str==null?"":str)+(str1==null?"":str1);
    }

    public static String getUserNameByAccount(String account,OrgUserMapper orgUserMapper) {
        OrgUserBean orgUserBean = orgUserMapper.selectByAccount(account);
        return orgUserBean.getUserName();
    }


    public static String transferZeorOrOne(String input) {
        if (input.equals("0")) {
            return "否";
        }
        if (input.equals("1")) {
            return "是";
        }
        return null;
    }

    /**
     * 账户下的用户状态检查
     * @param factory
     * @param code
     * @return
     */
    public static boolean checkAccountStatus(SessionFactory factory, String code) {
        UserInfoMapper mapper = factory.getMapper(UserInfoMapper.class);
        //查询账户下所有用户编号
        List<String> userNoList = mapper.selectUserNoByAccountNo(code);
        //判断是账户编号
        if (userNoList != null && userNoList.size() > 0) {
            int count = 0;
            //循环查询用户状态
            for (String userNo : userNoList) {
                if (!checkUserStatus(factory, userNo)) {
                    count++;
                }
            }
            //判断账户下用户全部停用或销户
            if (userNoList.size() == count) {
                return false;
            }
            return true;
        }
        //用户编号处理
        return checkUserStatus(factory, code);
    }

    /**
     * 用户编号状态检查
     * @param factory
     * @param userNo
     * @return
     */
    public static boolean checkUserStatus(SessionFactory factory, String userNo) {
        UserInfoMapper mapper = factory.getMapper(UserInfoMapper.class);
        int status = mapper.selectUserStatus(userNo);
        if (status != 1) {
            return false;
        }
        return true;
    }

    /**
     * 根据输入的字符串和整数生成新的字符串
     * @param prefix 输入的字符串前缀
     * @param number 输入的整数
     * @return 生成的新字符串
     */
    public static String generateCode(String prefix, int number) {
        // 将整数加1后格式化为至少两位的字符串
        String formattedNumber = String.format("%02d", number + 1);
        // 拼接前缀和格式化后的数字
        return prefix + formattedNumber;
    }
}